home *** CD-ROM | disk | FTP | other *** search
/ Gamers Delight 2 / Gamers Delight 2.iso / Aminet / game / role / pinfocom_3_0.lha / Source / page.c < prev    next >
C/C++ Source or Header  |  1992-10-22  |  8KB  |  272 lines

  1. /* page.c
  2.  *
  3.  *  ``pinfocom'' -- a portable Infocom Inc. data file interpreter.
  4.  *  Copyright (C) 1987-1992  InfoTaskForce
  5.  *
  6.  *  This program is free software; you can redistribute it and/or modify
  7.  *  it under the terms of the GNU General Public License as published by
  8.  *  the Free Software Foundation; either version 2 of the License, or
  9.  *  (at your option) any later version.
  10.  *
  11.  *  This program is distributed in the hope that it will be useful,
  12.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  *  GNU General Public License for more details.
  15.  *
  16.  *  You should have received a copy of the GNU General Public License
  17.  *  along with this program; see the file COPYING.  If not, write to the
  18.  *  Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  19.  */
  20.  
  21. /*
  22.  * $Header: RCS/page.c,v 3.0 1992/10/21 16:56:19 pds Stab $
  23.  */
  24.  
  25. #include <stdio.h>
  26.  
  27. #include "infocom.h"
  28.  
  29.  
  30. #ifndef malloc
  31. extern ptr_t malloc();
  32. #endif
  33.  
  34.  
  35. #ifndef MAX_PAGE_ENTRIES
  36. #define MAX_PAGE_ENTRIES    0x20
  37. #endif
  38. #define MIN_PAGE_ENTRIES    3
  39. #define NO_PAGE             0xFFFF
  40.  
  41. #define PG_ENTRY_LEN        (BLOCK_SIZE + sizeof(pg_table_t))
  42. #define PG_OFFSET(_n)       (pg_start+((long_word)(_n)*BLOCK_SIZE))
  43.  
  44.  
  45. /*
  46.  * This is a page table entry.  The page table is a circular linked
  47.  * list (using array indices instead of pointers to save space), where
  48.  * MRU_pg_p always references the most-recently-used page,
  49.  * pg_table[MRU_pg_p].prev is the least-recently-used page, and
  50.  * pg_table[MRU_pg_p].next, etc. are the rest of the pages.
  51.  */
  52. typedef struct
  53. {
  54.     word    page;
  55.     byte    next;
  56.     byte    prev;
  57. } pg_table_t;
  58.  
  59.  
  60. /*
  61.  * Variables:
  62.  *      pg_count    The number of pages in the page table (at least 2)
  63.  *
  64.  *      pg_table    Array of page table entries
  65.  *      pg_start    Ptr to the first page
  66.  *
  67.  *      cur_pg_p    The page currently being executed by the interpreter
  68.  *      MRU_pg_p    The most-recently-used page in the page table
  69.  *
  70.  * Notes:
  71.  *      Variables with suffixes "_page" refer to actual page numbers;
  72.  *      suffixes of "_pg_p" refer to "pointers" (array indices) into
  73.  *      the pg_table.
  74.  *
  75.  *      The cur_pg_p and MRU_pg_p will be the same if the program is
  76.  *      executing paged-in memory, but different if it's executing
  77.  *      resident memory (which isn't stored in the page table, of
  78.  *      course).
  79.  */
  80. static pg_table_t   *pg_table   = 0;
  81. static byte         *pg_start   = 0;
  82.  
  83. static word         pg_count    = 0;
  84. static word         cur_page    = 0;
  85. static word         MRU_pg_p    = 0;
  86.  
  87.  
  88. /*
  89.  * Initialize the page table.  Find out the maximum number of pages we
  90.  * can load, then allocate the memory and initialize the page table.
  91.  * Make sure we have at *least* MIN_PAGE_ENTRIES pages, or things will
  92.  * not work well.  Finally initialize the pointers correctly.
  93.  */
  94. void
  95. pg_init()
  96. {
  97.     extern file_t   file_info;
  98.     extern word     resident_blocks;
  99.  
  100.     pg_table_t *ptr;
  101.     int i;
  102.  
  103.     /*
  104.      * Find out how many pages we can successfully allocate (MAX_PAGES
  105.      * or the number of pages in the file, whichever is less, is the
  106.      * most we can use.
  107.      */
  108.     i = file_info.pages - resident_blocks + 1;
  109.     i = (i > MAX_PAGE_ENTRIES
  110.          ? MAX_PAGE_ENTRIES
  111.          : (i < MIN_PAGE_ENTRIES ? MIN_PAGE_ENTRIES : i));
  112.  
  113.     for (; i>=MIN_PAGE_ENTRIES && pg_start==0; i -= 0x0F)
  114.         pg_start = (byte *)malloc(i * PG_ENTRY_LEN);
  115.  
  116.     if (pg_start == 0)
  117.     {
  118.         i = MIN_PAGE_ENTRIES;
  119.         pg_start = (byte *)xmalloc(i * PG_ENTRY_LEN);
  120.     }
  121.     else
  122.     {
  123.         i += 0x0F;
  124.     }
  125.     pg_count = i;
  126.  
  127.     /*
  128.      * Find the start of the page table and entry sections.
  129.      */
  130.     pg_table = (pg_table_t *)pg_start;
  131.     pg_start += (pg_count * sizeof(pg_table_t));
  132.  
  133.     /*
  134.      * Initialize so that LRU=0, LRU[1]=1, etc.; this speeds up
  135.      * initialization.
  136.      */
  137.     for (i = 0, ptr = pg_table; i < (int)pg_count; ++i, ++ptr)
  138.     {
  139.         ptr->page = NO_PAGE;
  140.         ptr->next = i - 1;
  141.         ptr->prev = i + 1;
  142.     }
  143.  
  144.     MRU_pg_p = pg_count - 1;
  145.  
  146.     pg_table[0].next        = MRU_pg_p;
  147.     pg_table[MRU_pg_p].prev = 0;
  148. }
  149.  
  150.  
  151. /*
  152.  * Locate new_page in in the page table (or load it into the LRU block
  153.  * if it's not already in the table) and return a pointer to it.
  154.  */
  155. byte *
  156. fetch_page A1(word, new_page)
  157. {
  158.     pg_table_t *mp;
  159.     int pg_p;
  160.  
  161.     /*
  162.      * If the page we want is already the MRU page, then we're done...
  163.      *
  164.      * Some empirical research (printf's! :-) suggests that a full 33%
  165.      * of all swaps are done between the MRU and the next block.  So,
  166.      * to speed things up still further we check to see if that's the
  167.      * case and if so we don't swap.  This is a double gain since the
  168.      * same research suggests that many of these swaps are done
  169.      * continuously; i.e., block 1 and 2 swap, then swap back next
  170.      * call, then swap back again, etc.
  171.      */
  172.     pg_p = MRU_pg_p;
  173.     mp = &pg_table[pg_p];
  174.  
  175.     if ((new_page != mp->page)
  176.         && (new_page != pg_table[pg_p = mp->next].page))
  177.     {
  178.         pg_table_t *tp;
  179.  
  180.         /*
  181.          * Look through the page table (in no particular order) to see
  182.          * if the block we want is already loaded.  If it is,
  183.          * manipulate the list to make it the MRU.
  184.          */
  185.         for (tp = pg_table, pg_p = 0; pg_p < (int)pg_count; ++pg_p, ++tp)
  186.         {
  187.             if (new_page == tp->page)
  188.             {
  189.                 pg_table[tp->prev].next = tp->next;
  190.                 pg_table[tp->next].prev = tp->prev;
  191.  
  192.                 tp->next = MRU_pg_p;
  193.                 tp->prev = mp->prev;
  194.  
  195.                 mp->prev = pg_p;
  196.                 pg_table[tp->prev].next = pg_p;
  197.  
  198.                 MRU_pg_p = pg_p;
  199.  
  200.                 break;
  201.             }
  202.         }
  203.  
  204.         /*
  205.          * If we didn't find the page we want in the page table, then
  206.          * we'll have to load it.  Choose the LRU page to load it
  207.          * into, unless the LRU page is the currently executing page:
  208.          * this could happen if we do lots of get_byte() or get_word()
  209.          * calls (which are basically asyncronous memory requests...)
  210.          * between fix_pc() calls.
  211.          *
  212.          * Once we've loaded it, make the LRU into the MRU.  Since the
  213.          * list is circular we can do this by simply moving the MRU
  214.          * pointer over.  Note that if we had to move an extra space
  215.          * because the LRU is the current page then the current page
  216.          * will now be the 2nd MRU block, but who cares, that's
  217.          * probably a good thing...
  218.          */
  219.         if (pg_p == pg_count)
  220.         {
  221.             pg_p = mp->prev;
  222.             if (pg_table[pg_p].page == cur_page)
  223.                 pg_p = pg_table[pg_p].prev;
  224.  
  225.             load_page(new_page, 1, PG_OFFSET(pg_p));
  226.  
  227.             pg_table[pg_p].page = new_page;
  228.             MRU_pg_p = pg_p;
  229.         }
  230.     }
  231.  
  232.     return (PG_OFFSET(pg_p));
  233. }
  234.  
  235. void
  236. fix_pc()
  237. {
  238.     extern word     pc_page;
  239.     extern word     pc_offset;
  240.     extern word     resident_blocks;
  241.     extern byte     *prog_block_ptr;
  242.     extern byte     *base_ptr;
  243.  
  244.     /*
  245.      * This must be 32 bits long since 'pc_page'
  246.      * can have any value from $0000 to $FFFF.
  247.      */
  248.  
  249.     long_word       pc;
  250.  
  251.     /* The high bit of 'pc_offset' is actually a sign bit. */
  252.  
  253.     pc = ((long_word)pc_page * BLOCK_SIZE) + (signed_word)pc_offset;
  254.     pc_offset = pc % BLOCK_SIZE;
  255.     pc_page = pc / BLOCK_SIZE;
  256.  
  257.     /*
  258.      * If the page we want isn't the one we currently have, then get
  259.      * it: if it's in resident memory then set it directly otherwise
  260.      * fetch it from the page table.
  261.      */
  262.     if (pc_page != cur_page)
  263.     {
  264.         cur_page = pc_page;
  265.  
  266.         if (pc_page < resident_blocks)
  267.             prog_block_ptr = base_ptr + ((long_word)pc_page * BLOCK_SIZE);
  268.         else
  269.             prog_block_ptr = fetch_page(pc_page);
  270.     }
  271. }
  272.